package com.clf.data.redis.core.json;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.clf.data.redis.core.json.ops.*;
import com.clf.data.redis.core.json.ops.impl.*;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.lang.reflect.Constructor;
import java.util.List;

/**
 * 
 * 使用fastjson解析的redisTemplate
 * 改写redisTemplate部分方法，并设置解析器为fastjson
 * 添加部分方法
 * 具体API几乎和RedisTemplate相同，请参考spring的RedisTemplate
* @author <a href="mailto:zhudelie@zuozh.com">zhudelie</a>
* @version 
* @since JDK 1.6
* create on 2017年3月31日
* Copyright 2017 ZZJR All Rights Reserved.
*
*/
public class FastJsonRedisTemplate<K,V> extends RedisTemplate<K,V> {
	private static final Logger logger = LogManager.getLogger(FastJsonRedisTemplate.class);
	private static final Logger errorLogger = LogManager.getLogger("exceptionLog");
	private static final ThreadLocal<RedisSerializer<?>> serializers = new ThreadLocal<RedisSerializer<?>>();
	private static final ThreadLocal<Integer> notAutoRemove = new ThreadLocal<Integer>();  
	private static final ThreadLocal<Boolean> ignoreRedisException  = new ThreadLocal<Boolean>();
	private static final ThreadLocal<Integer> fronzeSetIgnore  = new ThreadLocal<Integer>();
	private static class DefaultFastJsonSerializer<T> extends FastJsonSerializerAbstractor<T>{
		@Override
		public T deserialize0(byte[] bytes) throws SerializationException {
			return (T)JSON.parse(bytes);
		}
	}
	public FastJsonRedisTemplate() {
		RedisSerializer<?> keySerializer = new KeySerializer();
		RedisSerializer<?> defaultFastJsonSerializer = new DefaultFastJsonSerializer();
		setKeySerializer(keySerializer);
		setValueSerializer(defaultFastJsonSerializer);
		setHashKeySerializer(keySerializer);
		setHashValueSerializer(defaultFastJsonSerializer);
		setDefaultSerializer(defaultFastJsonSerializer);
	}
	public FastJsonRedisTemplate(RedisConnectionFactory connectionFactory) {
		this();
		setConnectionFactory(connectionFactory);
		afterPropertiesSet();
	}
	/**
	 * 设置value返回值转化，如果想恢复默认的转化，则  setValueSerializer(getDefaultSerializer)，如果传入的是FastJsonRedisSerializer，则看是否自动撤销转化
	 */
	@Override
	public void setValueSerializer(RedisSerializer<?> serializer) {
		if(serializer instanceof DefaultFastJsonSerializer){
			removeConverType();
			super.setValueSerializer(serializer);
			return;
		}
		if(serializer instanceof FastJsonSerializerAbstractor){
			serializers.set(serializer);
		}else{
			super.setValueSerializer(serializer);
		}
	}
	/**
	 * 设置hash的value返回值转化，如果想恢复默认的转化，则  setHashValueSerializer(getDefaultSerializer)，如果传入的是FastJsonRedisSerializer，则看是否自动撤销转化
	 */
	@Override
	public void setHashValueSerializer(RedisSerializer<?> serializer) {
		if(serializer instanceof DefaultFastJsonSerializer){
			removeConverType();
			super.setHashValueSerializer(serializer);
			return;
		}
		if(serializer instanceof FastJsonSerializerAbstractor){
			serializers.set(serializer);
		}else{
			super.setHashValueSerializer(serializer);
		}
	}
	/**
	 * 设置返回值转换类型，调用redis命令后，转换类型将会自动回到默认的情况
	 * @param type
	 */
	public <T> void setConvertType(Class<T> type){
		setConvertType(type,true);
	}
	/**
	 * 设置返回值转换类型，调用redis命令后，设置转换类型后是否会自动回到默认的情况
	 * @param type
	 */
	public <T> void setConvertType(Class<T> type,boolean autoRemove){
		setValueSerializer(new FastJsonSerializer(type,autoRemove));
	}
	/**
	 * 设置返回值转换类型,用于泛型嵌套，调用redis命令后，转换类型将会自动回到默认的情况
	 * setConverType(new TypeReference&lt;List《Map《String，String》》(){});
	 * setConverType(new TypeReference《T》(String.class){},false);
	 * @param type
	 */
	public <T> void setConvertType(TypeReference<T> type){
		setConvertType(type,true);
	}
	/**
	 * 设置返回值转换类型，用于泛型嵌套，调用redis命令后，设置转换类型后是否会自动回到默认的情况
	 *  setConverType(new TypeReference《List《Map《String，String》》》(){},false);
	 *  setConverType(new TypeReference《List《T》》(String.class){},false);
	 * @param type
	 */
	public <T> void setConvertType(TypeReference<T> type,boolean autoRemove){
		setValueSerializer(new FastJsonSerializerType<T>(type,autoRemove));
	}
	/**
	 *  移除返回值转换类型
	 */
	public void removeConverType(){
		serializers.remove();
		notAutoRemove.remove();
	}
	/**
     * 忽略redis的异常，如果是，则执行redis下一条命令错误后，将不会抛出异常，但会打印日志
     * 注意，redis命令执行后，会自动失效

	 */
	public void ignoreRedisException(){
		setIgnoreRedisException0(true);
	}
	/**
     * 忽略redis的异常，如果是，则执行redis命令错误后，将不会抛出异常，但会打印日志
     * 注意，redis命令执行后，不会自动失效，一定要在finally里面调用endIgnoreRedisException()

	 */
	public void beginIgnoreRedisException(){	
		fronzeSetIgnore.set(1);
		ignoreRedisException.set(true);
	}
	/**
     * 强行取消忽略redis异常
	 */
	public void endIgnoreRedisException(){
		ignoreRedisException.set(false);
		fronzeSetIgnore.remove();
	}
	
	//-------------------------以下方法只是覆盖一些默认行为，无需关心--------------------------------
	/*
	 * 删除类型转换
	 */
	private void removeConverType0(){
		Integer notRemove = notAutoRemove.get();
		if(notRemove != null){
			return;
		}
		RedisSerializer<?> serializer = serializers.get();
		if(serializer == null){
			return;
		}
		FastJsonSerializerAbstractor<?> fs = (FastJsonSerializerAbstractor<?>)serializer;
		if(fs.autoRemoveConvert){
			removeConverType();
		}
	}
	/*  拦截value解析器
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisTemplate#getValueSerializer()
	 */
	@Override
	public RedisSerializer<?> getValueSerializer() {
		RedisSerializer<?> serializer = serializers.get();
		if(serializer == null){
			serializer = super.getValueSerializer();
		}
		return serializer;
	}
	/*拦截hashvalue解析器
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisTemplate#getHashValueSerializer()
	 */
	@Override
	public RedisSerializer<?> getHashValueSerializer() {
		RedisSerializer<?> serializer = serializers.get();
		if(serializer == null){
			serializer = super.getHashValueSerializer();
		}
		return serializer;
	}
	
	/*获得连接后额外处理
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisTemplate#preProcessConnection(org.springframework.data.redis.connection.RedisConnection, boolean)
	 */
	@Override
	protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
		return new DefaultStringRedisConnection(connection);
	}
	/*
	 * 是否忽略redis的异常，
	 * @param ignoreOrNot
	 */
	private boolean isIgnoreRedisException(){
		Boolean ignore = ignoreRedisException.get();
		return ignore == null?false:ignore;
	}
	
	/*
	 *设置是否忽略redis异常 
	 * @param isIgnoreOrNot
	 */
	private void setIgnoreRedisException0(boolean isIgnoreOrNot){
		if(fronzeSetIgnore.get() != null||notAutoRemove.get() != null){
			return;
		}
		ignoreRedisException.set(isIgnoreOrNot);
	}
	
	/*
	 *拦截execute方法，使之每次调用完后，删除类型转换解析器和忽略异常，变成默认的形式
	 */
	@Override
	public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
		if(isIgnoreRedisException()){
			try{
				return super.execute(action, exposeConnection, pipeline);
			}catch(Exception e){			
				if(logger.isErrorEnabled()){
					logger.error("redis拦截下来的异常",e);
					errorLogger.error("redis拦截下来的异常",e);
				}
				return null;
			}finally{
				removeConverType0();
				setIgnoreRedisException0(false);
			}
		}else{			
			try{
				return super.execute(action, exposeConnection, pipeline);
			}finally{
				removeConverType0();
			}
		}
	}
	/*
	 *拦截execute方法，使之每次调用完后，删除类型转换解析器，变成默认的形式
	 */
	@Override
	public <T> T execute(SessionCallback<T> session) {
		if(isIgnoreRedisException()){
			try{
				return super.execute(session);
			}catch(Exception e){
				if(logger.isErrorEnabled()){
					logger.error("redis拦截下来的异常",e);
					errorLogger.error("redis拦截下来的异常",e);
				}
				return null;
			}finally{
				removeConverType0();
				setIgnoreRedisException0(false);
			}
		}else{
			try{
				return super.execute(session);
			}finally{
				removeConverType0();
			}
		}	
	}

	@Override
	public List<Object> executePipelined(RedisCallback<?> action, RedisSerializer<?> resultSerializer) {
		if(isIgnoreRedisException()){
			try{
				return super.executePipelined(action, resultSerializer);
			}catch(Exception e){
				if(logger.isErrorEnabled()){
					logger.error("redis拦截下来的异常",e);
					errorLogger.error("redis拦截下来的异常",e);
				}
				return null;
			}finally{
				removeConverType0();
				setIgnoreRedisException0(false);
			}
		}else{
			try{
				return super.executePipelined(action, resultSerializer);
			}finally{
				removeConverType0();
			}
		}
	}

	@Override
	public List<Object> executePipelined(SessionCallback<?> session, RedisSerializer<?> resultSerializer) {
		if(isIgnoreRedisException()){
			try{
				return super.executePipelined(session, resultSerializer);
			}catch(Exception e){
				if(logger.isErrorEnabled()){
					logger.error("redis拦截下来的异常",e);
					errorLogger.error("redis拦截下来的异常",e);
				}
				return null;
			}finally{
				removeConverType0();
				setIgnoreRedisException0(false);
			}
		}else{
			try{
				return super.executePipelined(session, resultSerializer);
			}finally{
				removeConverType0();
			}
		}
	}
	/*
	 * cglib代理
	 */
	private Object proxy(Object ops) {
		ProxyFactory factory = new ProxyFactory(ops);
		factory.setOpaque(true);
		factory.setOptimize(true);
		factory.addAdvice(new MethodInterceptor() {
			@Override
			public Object invoke(MethodInvocation invocation) throws Throwable {
				try{
					//取消自动删除类型转换，防止execute之后自动变成默认类型转换器
					notAutoRemove();
					return invocation.proceed();
				}finally{
					//强行删除类型转换器，使之变回默认类型转换
					removeConverType();
					setIgnoreRedisException0(false);
				}
			}
		});
		return factory.getProxy();
	}
	/*
	 * 不自动删除类型转换
	 */
	private void notAutoRemove() {
		notAutoRemove.set(1);
	}
	
	FastJsonValueOperations<K,V> opsValue;
	//拦截ops
	@Override
	public  FastJsonValueOperations<K,V> opsForValue() {
		if(opsValue != null){
			return opsValue;
		}
		return opsValue = (FastJsonValueOperations<K,V>) proxy( new FastJsonValueOperationsImpl(super.opsForValue(),  this));
	}
	
	FastJsonHashOperations opsHash;
	//拦截ops
	@Override
	public <HK,HV> FastJsonHashOperations<K,HK,HV> opsForHash() {
		if(opsHash != null){
			return opsHash;
		}
		// TODO Auto-generated method stub
		return opsHash = (FastJsonHashOperations) proxy(new FastJsonHashOperationsImpl(super.opsForHash(), this));
	}
	FastJsonListOperations<K,V> opsList;
	//拦截ops
	@Override
	public FastJsonListOperations<K,V> opsForList() {
		if(opsList != null){
			return opsList;
		}
		return opsList = (FastJsonListOperations<K,V>) proxy(new FastJsonListOperationsImpl( super.opsForList(),this));
	}
	FastJsonSetOperations<K,V> opsSet;
	//拦截ops
	@Override
	public FastJsonSetOperations<K,V> opsForSet() {
		if(opsSet != null){
			return opsSet;
		}
		return opsSet = (FastJsonSetOperations<K,V>) proxy(new  FastJsonSetOperationsImpl(super.opsForSet(),this));
	}
	FastJsonZSetOperations<K,V> opsZset;
	//拦截ops
	@Override
	public FastJsonZSetOperations<K,V> opsForZSet() {
		if(opsZset != null){
			return opsZset;
		}
		return opsZset = (FastJsonZSetOperations<K,V>) proxy(new FastJsonZSetOperationsImpl(super.opsForZSet(),this));
	}
	Constructor<FastJsonBoundHashOperations> bopsHash;
	
	//拦截ops
	@Override
	public <HK,HV> FastJsonBoundHashOperations<K,HK,HV> boundHashOps(K key) {	
		if(this.bopsHash!=null){
			try {
				return bopsHash.newInstance(super.boundHashOps(key), this);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		FastJsonBoundHashOperations bopsHash = (FastJsonBoundHashOperations) proxy(new FastJsonBoundHashOperationsImpl( super.boundHashOps(key), this));
		try {
			this.bopsHash = (Constructor<FastJsonBoundHashOperations>) bopsHash.getClass().getConstructor(BoundHashOperations.class,this.getClass());
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
		return bopsHash;
	}
	Constructor<FastJsonBoundListOperations<K,V>> bopsList;
	//拦截ops
	@Override
	public FastJsonBoundListOperations<K,V> boundListOps(K key) {
		if(bopsList != null){		
			try {
				return bopsList.newInstance(super.boundListOps(key),this);
			}catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		FastJsonBoundListOperations<K,V> bopsList = (FastJsonBoundListOperations) proxy( new FastJsonBoundListOperationsImpl(super.boundListOps(key),this));
		try{
			this.bopsList = (Constructor<FastJsonBoundListOperations<K,V>>) bopsList.getClass().getConstructor(BoundListOperations.class,this.getClass());
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
		return bopsList;
	}
	Constructor<FastJsonBoundSetOperations<K,V>> bopsSet;
	//拦截ops
	@Override
	public FastJsonBoundSetOperations<K,V> boundSetOps(K key) {
		if(bopsSet != null){
			try{
				return bopsSet.newInstance(super.boundSetOps(key),this);
			}catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		FastJsonBoundSetOperations<K,V> bopsSet = (FastJsonBoundSetOperations<K,V>) proxy( new FastJsonBoundSetOperationsImpl(super.boundSetOps(key),this));
		try{
			this.bopsSet = (Constructor<FastJsonBoundSetOperations<K, V>>) bopsSet.getClass().getConstructor(BoundSetOperations.class,this.getClass());
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
		return bopsSet;
	}
	Constructor<FastJsonBoundValueOperations<K,V>> bOpsValue;
	//拦截ops
	@Override
	public FastJsonBoundValueOperations<K,V> boundValueOps(K key) {
		if(this.bOpsValue!=null){
			try {
				return bOpsValue.newInstance(super.boundValueOps(key), this);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		FastJsonBoundValueOperations<K,V> bOpsValue = (FastJsonBoundValueOperations) proxy(new FastJsonBoundValueOperationsImpl( super.boundValueOps(key), this));
		try {
			this.bOpsValue = (Constructor<FastJsonBoundValueOperations<K,V>>) bOpsValue.getClass().getConstructor(BoundValueOperations.class,this.getClass());
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
		return bOpsValue;
	}
	Constructor<FastJsonBoundZSetOperations<K,V>> bOpsZSet;
	//拦截ops
	@Override
	public FastJsonBoundZSetOperations<K,V> boundZSetOps(K key) {
		if(bOpsZSet != null){
			try{
				return bOpsZSet.newInstance(super.boundZSetOps(key),this);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		FastJsonBoundZSetOperations<K,V> bOpsZSet = (FastJsonBoundZSetOperations<K,V>) proxy(new FastJsonBoundZSetOperationsImpl(super.boundZSetOps(key),this));
		try {
			this.bOpsZSet = (Constructor<FastJsonBoundZSetOperations<K, V>>) bOpsZSet.getClass().getConstructor(BoundZSetOperations.class,this.getClass());
		}  catch (Exception e) {
			throw new RuntimeException(e);
		} 
		return bOpsZSet;
	}
}
